﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Marmat.Forms.Skin;
using System.Drawing.Drawing2D;
using System.Drawing.Design;
using Marmat.Forms;
using Marmat.Foundation;

namespace Marmat.Template.WinForm
{
    /// <summary>
    /// 编辑窗体的状态控件。
    /// </summary>
    [ToolboxItem(true)]
    public class EditFormStatusControl : System.Windows.Forms.Control, IBindingField
    {
        private string status;
        private Dictionary<IButtonItem, Rectangle> buttonRectangleCollection = new Dictionary<IButtonItem, Rectangle>();
        private List<(Func<bool>, IEnumerable<ButtonItem>)> buttonRegulation = new List<(Func<bool>, IEnumerable<ButtonItem>)>();
        private IButtonItem mouseButtonItem;
        private IButtonItem mousedownButtonItem;

        /// <summary>
        /// 初始化 Marmat.Template.WinForm.EditFormStatusControl 类的新实例。
        /// </summary>
        public EditFormStatusControl()
        {
            SetStyle(ControlStyles.ResizeRedraw, true); //尺寸变化后绘制
            SetStyle(ControlStyles.Opaque, false); //改善绘图质量
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);

            InitializeComponent();
            this.StatusCollection = new List<string>();
        }


        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // EditFormStatusControl
            // 
            this.BackColor = System.Drawing.Color.White;
            this.Font = new Font("微软雅黑", 9.0f);
            this.Dock = System.Windows.Forms.DockStyle.Top;
            this.ResumeLayout(false);
        }

        /// <summary>
        /// 获取或设置编辑窗体的控制器，用于点击事件的前置处理以及后续的数据加载。
        /// </summary>
        [DefaultValue(null)]
        public IEditFormController Controller { get; set; }

        /// <summary>
        /// 状态的集合。
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        public List<string> StatusCollection { get; }

        private List<IButtonItem> buttonCollection = new List<IButtonItem>();

        /// <summary>
        /// 当前的状态。
        /// </summary>
        [DefaultValue(null)]
        public string Status
        {
            get => status;
            set
            {
                status = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 返回或设置控件的绑定Table字段
        /// </summary>
        [Localizable(true)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("数据")]
        [DisplayName("(BindingField)")]
        [Description("返回或设置控件的绑定Table字段")]
        public virtual BindingField BindingField
        {
            get
            {
                if (this._BindingField == null)
                    this._BindingField = new Marmat.Forms.BindingField(this, "Status", null, null, true, DataSourceUpdateMode.OnValidation);
                return this._BindingField;
            }
        }
        private BindingField _BindingField;

        /// <summary>
        /// 获取或设置控件的背景色。
        /// </summary>
        [DefaultValue(typeof(Color), "White")]
        public override Color BackColor { get => base.BackColor; set => base.BackColor = value; }

        /// <summary>
        /// 获取或设置哪些控件边框停靠到其父控件并确定控件如何随其父级一起调整大小。
        /// </summary>
        [DefaultValue(typeof(DockStyle), "Top")]
        public override DockStyle Dock { get => base.Dock; set => base.Dock = value; }

        /// <summary>
        /// 获取控件的默认大小。
        /// </summary>
        protected override Size DefaultSize => new Size(500, 35);

        /// <summary>
        /// 获取或设置控件显示的文字的字体。
        /// </summary>
        [DefaultValue(typeof(Font), "微软雅黑, 9pt")]
        public override Font Font { get => base.Font; set => base.Font = value; }

        /// <summary>
        /// 获取或设置与此控件关联的文本。
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override string Text { get => base.Text; set => base.Text = value; }


        /// <summary>
        ///  将 BindingField 属性重置为其默认值。
        /// </summary>
        protected internal void ResetBindingField()
        {
            this.BindingField.DataSource = null;
            this.BindingField.DataMember = null;
        }

        /// <summary>
        /// 是否应该序列化 BindingField 属性。
        /// </summary>
        /// <returns></returns>
        protected internal bool ShouldSerializeBindingField()
        {
            return !(this.BindingField.DataSource == null && string.IsNullOrEmpty(this.BindingField.DataMember));
        }

        /// <summary>
        /// 添加按钮
        /// </summary>
        /// <param name="name">按钮的名称</param>
        public void AddButton(string name)
        {
            this.buttonCollection.Add(new ButtonItem(name));
        }

        /// <summary>
        /// 添加按钮
        /// </summary>
        /// <param name="name">按钮的名称</param>
        /// <param name="text">按钮的文本</param>
        public void AddButton(string name, string text)
        {
            this.buttonCollection.Add(new ButtonItem(name, text));
        }

        /// <summary>
        /// 覆盖现有状态的集合体。
        /// </summary>
        /// <param name="statusCollection">需要覆盖的状态集</param>
        public void SetStatusCollection(params string[] statusCollection)
        {
            this.StatusCollection.Clear();
            this.StatusCollection.AddRange(statusCollection);
            this.Invalidate();
        }

        /// <summary>
        /// 清除按钮集的规则。
        /// </summary>
        public void ClearButtonRegulation()
        {
            this.buttonCollection.Clear();
            this.buttonRegulation.Clear();
            this.Invalidate();
        }

        /// <summary>
        /// 设置按钮集的规则(根据该控件的 Status 属性与传入的属性对比)。
        /// </summary>
        public void AddButtonRegulation(string status, params ButtonItem[] buttonCollection)
        {
            AddButtonRegulation(() => string.Equals(this.Status, status, StringComparison.OrdinalIgnoreCase), buttonCollection);
        }

        /// <summary>
        /// 设置按钮集的规则。
        /// </summary>
        public void AddButtonRegulation(Func<bool> regulation, params ButtonItem[] buttonCollection)
        {
            buttonRegulation.Add((regulation, buttonCollection));
        }

        /// <summary>
        /// 设置按钮集的规则。
        /// </summary>
        public void AddRegulation(Func<bool> regulation, params ButtonItem[] buttonCollection)
        {
            buttonRegulation.Add((regulation, buttonCollection));
        }

        /// <summary>
        /// 根据预设的规则重新构建按钮集。
        /// </summary>
        public void Builder()
        {
            this.buttonCollection.Clear();
            foreach (var item in this.buttonRegulation)
            {
                if (item.Item1() == true)
                    this.buttonCollection.AddRange(item.Item2);
            }
            this.Invalidate();
        }

        /// <summary>
        /// 清除所有按钮
        /// </summary>
        public void Clear()
        {
            this.buttonCollection.Clear();
        }

        /// <summary>
        /// 获取按钮对象。
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Marmat.Foundation.IButtonItem this[string name] => this.buttonCollection.FirstOrDefault(x => x.Name == name);

        /// <summary>
        /// 项点击时发生。
        /// </summary>
        public event EventHandler<EditFormStatusControlItemClickArgs> ItemClick;

        /// <summary>
        /// 引发 System.Windows.Forms.Control.Paint 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            int left = -1;
            buttonRectangleCollection.Clear();
            using var boderColor = new Pen(Color.FromArgb(206, 212, 218));
            foreach (var button in this.buttonCollection)
            {
                Rectangle rect = new Rectangle(left, -1, 100, this.Height + 2);
                Rectangle text_rect = rect;
                buttonRectangleCollection[button] = rect;
                Color backColor = this.BackColor;
                if (mousedownButtonItem == button)
                {
                    backColor = Color.FromArgb(210, 210, 210);
                    text_rect = new Rectangle(rect.X + 1, rect.Y + 1, rect.Width, rect.Height);
                }
                else if (mouseButtonItem == button)
                {
                    backColor = Color.FromArgb(233, 236, 239);
                }
                using SolidBrush brush = new SolidBrush(backColor);
                e.Graphics.FillRectangle(brush, rect);
                e.Graphics.DrawRectangle(boderColor, rect);

                TextRenderer.DrawText(e.Graphics, button.Caption ?? button.Name, this.Font, text_rect, this.ForeColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
                left += rect.Width;
            }
            List<(string status, int widht)> dic = new List<(string status, int widht)>();

            for (int index = 0; index < this.StatusCollection.Count; index++)
            {
                var status = this.StatusCollection[index];
                int item_width = TextRenderer.MeasureText(status, this.Font).Width + 10 * 2;
                if (index == 0)
                    item_width += 20;
                if (index > 0)
                    item_width += 10;
                if (index == this.StatusCollection.Count - 1)
                    item_width += 10;
                dic.Add((status, item_width));
            }

            left = this.Width - dic.Sum(x => x.widht);
            for (int index = 0; index < dic.Count; index++)
            {
                var item = dic[index];
                GraphicsPath path;
                Color backColor = Color.White;
                Color foreColor = Color.FromArgb(173, 181, 189);
                Font font = new Font(this.Font, FontStyle.Bold);
                Rectangle rect = new Rectangle(left, -1, item.widht, this.Height);
                Rectangle text_rect = rect;
                if (index == 0)
                    path = rect.GetBreadcrumbStartNodePath(10);
                else if (index == dic.Count - 1)
                {
                    path = rect.GetBreadcrumbEndNodePath(10);
                    text_rect = new Rectangle(rect.X + 10, rect.Y, rect.Width - 10, rect.Height);
                }
                else
                {
                    path = rect.GetBreadcrumbMiddleNodePath(10);
                    text_rect = new Rectangle(rect.X + 10, rect.Y, rect.Width, rect.Height);
                }
                if (item.status == this.Status)
                {
                    foreColor = Color.FromArgb(124, 123, 173);
                    backColor = Color.FromArgb(233, 236, 239);
                }
                using var brush = new SolidBrush(backColor);
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                e.Graphics.FillPath(brush, path);
                e.Graphics.DrawPath(boderColor, path);
                TextRenderer.DrawText(e.Graphics, item.status, font, text_rect, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding);
                left += item.widht;
            }
            e.Graphics.DrawLine(boderColor, 0, this.Height - 1, this.Width, this.Height - 1);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            var item = this.buttonRectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (item != this.mouseButtonItem)
            {
                this.mouseButtonItem = item;
                this.Invalidate();
            }
            this.Cursor = this.mouseButtonItem is not null ? Cursors.Hand : Cursors.Default;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            var item = this.buttonRectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (item != this.mousedownButtonItem)
            {
                this.mousedownButtonItem = item;
                this.Invalidate();
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (this.mousedownButtonItem is not null)
            {
                this.mousedownButtonItem = null;
                this.Invalidate();
            }
            var item = this.buttonRectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (item is not null)
            {
                this.OnItemClick(new EditFormStatusControlItemClickArgs(item));
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            this.Invalidate();
        }

        /// <summary>
        /// 引发 OnItemClick 事件。
        /// </summary>
        /// <param name="e">包含事件数据的 EditFormStatusControlItemClickArgs。</param>
        public void OnItemClick(EditFormStatusControlItemClickArgs e)
        {
            this.Select();
            bool execution = false;
            this.OnValidated(EventArgs.Empty);
            if (this.Controller is not null)
            {
                bool hasChanges = this.Controller.HasChanges();
                bool result = this.Controller.EditForm.Save(false);
                if (!hasChanges || result)
                    execution = true;
            }
            else
                execution = true;
            if (execution)
            {
                this.ItemClick?.Invoke(this, e);
                if (!e.Cancel)
                    this.Controller?.LoadDataObject();
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            this.mouseButtonItem = null;
            this.Invalidate();
        }
    }
}
